/**
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { computed, ref } from 'vue';
import { DataGridProps } from '../data-grid.props';
import { UseGroupData, VisualData } from './types';
import { ColumnRenderContext } from './use-column';

interface GroupingItem {
    field: string;
    title: string;
    value: any;
    details: any[];
    layer: number;
    nestGroup?: Map<any, GroupingItem>;
    groupSummaries: Map<string, number>;
}

export function useGroupData(
    props: DataGridProps
): UseGroupData {
    const identifyField = ref(props.idField);

    const summaryOptions = ref(props.summary);

    const groupOptions = ref(props.group);
    const groupFields = ref(props.group?.groupFields || []);
    const shouldGroupingData = ref(props.group?.enable || false);

    const groupSummaryFields = computed(() => {
        const options = summaryOptions.value;
        return options?.groupFields || [];
    });

    const shouldShowSummary = computed(() => {
        const options = groupOptions.value;
        return options && options.showSummary;
    });

    const groupedData = new Map<any, GroupingItem>();
    let flatGroupedData: any[] = [];
    let groupedPath = '';

    function processGroupTo(
        groupObject: Map<any, GroupingItem>, rawData: any[],
        groupLayer: number, groupFields: string[], columnContext: ColumnRenderContext
    ) {
        const groupField = groupFields[groupLayer];
        if (groupField) {
            rawData.reduce((groupResult: Map<any, GroupingItem>, rawDataItem: any, index: number) => {
                const groupingValue = rawDataItem[groupField] as any;
                let groupingItem = groupResult.get(groupingValue);
                if (!groupingItem) {
                    const groupSummaries = groupSummaryFields.value.reduce((sumaries: Map<string, number>, summaryField: string) => {
                        sumaries.set(summaryField, 0);
                        return sumaries;
                    }, new Map<string, number>());
                    groupingItem = {
                        field: groupField,
                        title: columnContext.primaryColumnsMap.get(groupField)?.title || groupField,
                        value: groupingValue, layer: groupLayer, details: [], groupSummaries
                    };
                    groupResult.set(groupingValue, groupingItem);
                }
                if (groupSummaryFields.value) {
                    groupSummaryFields.value.forEach((summaryField: string) => {
                        if (groupingItem) {
                            const summaryFieldValue = groupingItem.groupSummaries.get(summaryField) || 0;
                            groupingItem.groupSummaries.set(summaryField, summaryFieldValue + rawDataItem[summaryField]);
                        }
                    });
                }
                groupingItem.details.push(rawDataItem);
                return groupResult;
            }, groupObject);
            if (groupLayer < groupFields.length - 1) {
                groupObject.forEach((groupingItem: GroupingItem, groupingValue: any) => {
                    groupingItem.nestGroup = new Map<any, GroupingItem>();
                    processGroupTo(groupingItem.nestGroup, groupingItem.details, groupLayer + 1, groupFields, columnContext);
                    groupingItem.details = [];
                });
            }
        }
    }

    function toFlattenGroupedObject(targetGroupingObject: Map<any, GroupingItem>) {
        const groupingData: any[] = [];
        targetGroupingObject.forEach((groupingItem: GroupingItem) => {
            const virtualDataItem = {
                __fv_data_grid_group_collapse__: false,
                __fv_data_grid_group_field__: groupingItem.field,
                __fv_data_grid_group_layer__: groupingItem.layer,
                __fv_data_grid_group_row__: true,
                __fv_data_grid_group_value__: groupingItem.value
            } as Record<string, any>;
            let total = 0;
            groupingData.push(virtualDataItem);
            if (groupingItem.nestGroup) {
                const nestGroupingData = toFlattenGroupedObject(groupingItem.nestGroup);
                groupingData.push(...nestGroupingData);
                total += nestGroupingData.length;
            }
            if (groupingItem.details && groupingItem.details.length) {
                groupingData.push(...groupingItem.details);
                total += groupingItem.details.length;
            }
            if (shouldShowSummary.value) {
                const virtualSummaryDataItem = {
                    __fv_data_grid_group_field__: groupingItem.field,
                    __fv_data_grid_group_layer__: groupingItem.layer,
                    __fv_data_grid_group_summary__: true
                } as Record<string, any>;
                virtualSummaryDataItem[identifyField.value] = `summary_of_${groupingItem.field}_${groupingItem.value}`;
                virtualSummaryDataItem[groupingItem.field] = Array.from(groupingItem.groupSummaries.entries()).reduce(
                    (summaryResult: string, [summaryField, summaryValue]) => {
                        return `${summaryResult} ${summaryField} total:${summaryValue} `;
                    },
                    ''
                );
                groupingData.push(virtualSummaryDataItem);
            }
            if (groupingItem.groupSummaries && groupingItem.groupSummaries.size) {
                groupingItem.groupSummaries.forEach((summaryValue: number, summaryField: string) => {
                    virtualDataItem[summaryField] = summaryValue;
                });
            }
            virtualDataItem[identifyField.value] = `group_of_${groupingItem.field}_${groupingItem.value}`;
            virtualDataItem[groupingItem.field] = `${groupingItem.title}:${groupingItem.value} (${total})`;
        });
        groupingData.forEach((dataItem: any, index: number) => {
            dataItem.__fv_data_index__ = index;
        });
        return groupingData;
    }

    function generateGroupData(groupFields: string[], rawData: any[], columnContext: ColumnRenderContext): any[] {
        if (!groupFields || groupFields.length === 0) {
            return rawData;
        }

        const fieldsPath = groupFields.join(',');
        const hasGroupingPathChanged = fieldsPath !== groupedPath;
        if (hasGroupingPathChanged) {
            groupedData.clear();
            flatGroupedData = [];
            groupedPath = fieldsPath;
            processGroupTo(groupedData, rawData, 0, groupFields, columnContext);
            flatGroupedData = toFlattenGroupedObject(groupedData);
        }

        return flatGroupedData;
    }

    function collpaseGroupIconClass(groupRow: VisualData) {
        const classObject = {
            'f-icon': true,
            'f-icon-arrow-chevron-down': true,
            'fv-grid-group-row-icon-collapse': groupRow.collapse
        } as Record<string, boolean>;
        return classObject;
    }

    return { collpaseGroupIconClass, generateGroupData, groupFields, shouldGroupingData };
}
